from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relation, backref
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey
from path import path

import filter_series

# use 10MB of data to generate file hash
HASH_MAX_BYTES = 10 * 1024

def get_home_dir():
    import os
    # ...works on at least windows and linux.
    # In windows it points to the user's folder
    # (the one directly under Documents and Settings, not My Documents)

    # In windows, you can choose to care about local versus roaming profiles.
    # You can fetch the current user's through PyWin32.
    #
    # For example, to ask for the roaming 'Application Data' directory:
    # (CSIDL_APPDATA asks for the roaming, CSIDL_LOCAL_APPDATA for the local one)
    #
    # (See microsoft references for further CSIDL constants)
    try:
        from win32com.shell import shellcon, shell
        homedir = shell.SHGetFolderPath(0, shellcon.CSIDL_APPDATA, 0, 0)
    except ImportError:
        # quick semi-nasty fallback for non-windows/win32com case
        homedir = os.path.expanduser("~")
    return homedir


Base = declarative_base()
class TVEpisodeFileFactory(object):
    def __init__(self, session):
        self.session = session


    def get_tvfile_or_create(self, file, show, episodes):
        from datetime import datetime

        tvfile = self.session.query(TVEpisodeFile).filter_by(filepath=file).first()
        if tvfile is None:
            md5_hex = path(file).read_md5_hex(max_bytes=HASH_MAX_BYTES)
            tvfile = self.session.query(TVEpisodeFile).filter_by(md5_hex=md5_hex).first()
            if tvfile is None:
                print 'adding: %s' % str(file)
                tvfile = TVEpisodeFile(file, 
                                        md5_hex=md5_hex, 
                                        show=show, 
                                        episodes=episodes)
                self.session.add(tvfile)
            else:
                tvfile.md5_hex = md5_hex
        return tvfile


class TVEpisodeFile(Base):
    __tablename__ = 'videofiles'

    id = Column(Integer, primary_key=True)
    filepath = Column(String)
    md5_hex = Column(String)
    created = Column(DateTime)
    modified = Column(DateTime)
    show = Column(String)
    episodes = relation('TVEpisode', order_by='TVEpisode.episode', backref='videofile')

    def __init__(self, filepath, md5_hex=None, show='', episodes=None):
        from datetime import datetime

        self.filepath = filepath
        if md5_hex is None:
            self.md5_hex = path(self.filepath).read_md5_hex(max_bytes=HASH_MAX_BYTES)
        else:
            self.md5_hex = md5_hex
        self.created = datetime.now()
        self.modified = datetime.fromtimestamp(path(self.filepath).mtime)

        self.show = show

        self.episodes = []

        if episodes:
            self.update_episodes(episodes)

    
    def update_episodes(self, episodes):
        for new_episode in episodes:
            episode_exists = False
            for episode in self.episodes:
                if new_episode[0] == episode.season and \
                    new_episode[1] == episode.episode:
                    episode_exists = True
                    break
            if not episode_exists:
                self.episodes.append(TVEpisode(*new_episode))


    def __repr__(self):
       return "<TVEpisodeFile(%s)>" % (','.join(["'%s'" % (str(x)) for x in [self.filepath, self.md5_hex, self.show, self.episodes]]))


class TVEpisode(Base):
    __tablename__ = 'tvepisodes'
    id = Column(Integer, primary_key=True)
    season = Column(Integer)
    episode = Column(Integer)

    videofile_id = Column(Integer, ForeignKey('videofiles.id'))


    def __init__(self, season, episode):
        self.season = int(season)
        self.episode = int(episode)

    
    def __repr__(self):
        return '<TVEpisode(S%02dE%02d)>' % (self.season, self.episode)


class TVEpisodeScraper(object):
    def __init__(self, options=None):
        import yaml

        self.home_dir = path(get_home_dir())
        self.config_path = self.home_dir / path('.video_scraperrc')

        print 'loading config from: %s' % str(self.config_path)
        config = yaml.load(self.config_path.bytes()) or {}

        self.watch_list = [str(x) for x in config.get('shows', [])]
        self.db_path = config.get('db_path', self.home_dir / path('video_scraper/data.db'))
        self.data_dirs = [path(x) for x in config.get('data_dirs', [])]
        self.db_init()


    def db_init(self):
        from sqlalchemy import create_engine
        from sqlalchemy.orm import sessionmaker

        self.engine = create_engine('sqlite:///%s' % str(self.db_path), echo=False)
        self.metadata = Base.metadata
        self.metadata.create_all(self.engine) 

        self.Session = sessionmaker(bind=self.engine)
        self.session = self.Session()

        self.factory = TVEpisodeFileFactory(self.session)


    def get_file_list(self):
        from sqlalchemy.orm import eagerload

        return self.session.query(TVEpisodeFile).options(eagerload('episodes')).all()


    def scrape_dir(self):
        from datetime import datetime

        for data_dir in self.data_dirs:
            print 'scraping dir:', data_dir
            for file in data_dir.walkfiles('*.avi'):
                show_matched = False
                for show in self.watch_list:
                    parser = filter_series.SerieParser()
                    parser.name = show
                    parser.data = str(file.name)
                    parser.parse()
                    if parser.valid:
                        show_matched = True
                        break
                if show_matched:
                    #print 'show matched: %s %02dE%02d -> %s' % (show, parser.season, parser.episode, str(file.name))
                    episodes = [(parser.season, x) for x in parser.episodes]
                    tv_episode = self.factory.get_tvfile_or_create(file, 
                                        show,
                                        episodes)
        self.session.commit()
